// (win32) profiler
// $Id: profiler.h 395 2010-03-26 14:01:03Z Oleg.Bulychov $
#pragma once

#if defined(_MSC_VER)
#include <intrin.h>
#endif

class KProfilerBase
{
    int64_t iStart_;
    int64_t iTimeStart_;
    char const* szText_;
public:
    explicit KProfilerBase(char const* szText) NO_THROW_():
        szText_(szText),
        iStart_(),
        iTimeStart_()
    {}
//.............................................................................
    void enter() NO_THROW_();
//.............................................................................
    void leave(bool bSilent, bool bForce, bool bRoot) NO_THROW_();
//.............................................................................
    //this call should fix rdtsc problem
    static void Set_Thread_Processor(int nProcessor) NO_THROW_();//:zero-based!
//.............................................................................
    static void Set_Max_Priority() NO_THROW_();
};
//__________________________________________________________________________________
//__________________________________________________________________________________
class KProfiler:
    public KProfilerBase
{
    bool silent_;//:siletium est aurum
    bool force_;
    bool enter_;
public:
    explicit KProfiler(char const* szText, bool bSilent = false,
        bool bForce = false) NO_THROW_();
//.............................................................................
    ~KProfiler() NO_THROW_()
    {
        leave();
    }
//.............................................................................
    void leave() NO_THROW_();
};
//__________________________________________________________________________________
//__________________________________________________________________________________
class KProfilerServer:
    public KProfilerBase
{
    bool enter_;
    int id_;
public:
    explicit KProfilerServer(int nId, char const* szText) NO_THROW_():
        KProfilerBase(szText),
        enter_(),
        id_(nId)
    {
        ASSERT_(id_ > 0);
    }
//.............................................................................
    ~KProfilerServer() NO_THROW_()
    {
        leave();
    }
//.............................................................................
    void enter() NO_THROW_();
//.............................................................................
    void leave(bool bSilent = false) NO_THROW_();
};
//__________________________________________________________________________________
//__________________________________________________________________________________
inline int64_t Get_CPU_Ticks() NO_THROW_()
{//:note: may be wrong on multiprocessor systems
#if defined(_MSC_VER)
    return __rdtsc();
#elif defined __GNUC__
    volatile uint32_t Low, High;
    asm volatile ("rdtsc" : "=a" (Low), "=d" (High));
    return (uint64_t(High) << 32) + Low;
#else
#error RDTSC not supported
#endif
}
//__________________________________________________________________________________
class KTicksMonitor
{
    int64_t start_;
    int64_t ticks_;
    int enter_;
    char const* szText_;
public:
    explicit KTicksMonitor(char const* szText) NO_THROW_():
        start_(),
        ticks_(),
        enter_(),
        szText_(szText)
    {}
//.............................................................................
    void report(std::ostream& os) const
    {
        os << szText_ << " ticks: " << ticks_ / 1000 << lf;
    }
//.............................................................................
    void enter() NO_THROW_()
    {
        if (!enter_++)
        {
            start_ = Get_CPU_Ticks();
        }
    }
//.............................................................................
    void leave() NO_THROW_()
    {
        if (!--enter_)
        {
            ticks_ += Get_CPU_Ticks() - start_;
        }
    }
};
//__________________________________________________________________________________
class KTicksMonitorLock
{
    KTicksMonitor& tm_;
public:
    KTicksMonitorLock(KTicksMonitor& tm) NO_THROW_(): tm_(tm)
    {
        tm.enter();
    }
    ~KTicksMonitorLock() NO_THROW_()
    {
        tm_.leave();
    }
};

//EOF!
